/**
 * FileName：ServiceCaller.java
 * Date：2010-12-5
 * User: heng.dong
 */
package com.smileus.integration.caller;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ibatis.common.util.Page;
import com.smileus.integration.caller.impl.CxfSyncCaller;
import com.smileus.util.JSONUtil;

/**
 * FileName：ServiceCaller.java Date：2010-12-5 User: heng.dong 
 */
/**
 * 同步调用模式
 * 
 * @author dongheng
 */
@SuppressWarnings("unchecked")
public class SyncServiceCaller {
	private static final Logger logger = Logger.getLogger(SyncServiceCaller.class);
	// 可以在spring中配置，通过spring注入
	private static Map<String, Caller> callerMap = new HashMap<String, Caller>() {
		private static final long serialVersionUID = 1L;
		{
			put("cxf", new CxfSyncCaller());
		}
	};

	/**
	 * serviceId形式如：userService.add，userService为当前机器上spring bean
	 * id，add为当前bean的方法；
	 * 
	 * 解析serviceId，根据serviceId来确定当期服务该调用何种类型的caller
	 * 
	 * @param serviceId
	 * @return Caller
	 * @throws
	 */

	public static Caller getCaller(String serviceId) {
		// TODO: 可以通过serviceId取到所在服务系统的相关信息
		Caller caller = callerMap.get("cxf");
		// 解析serviceId，根据serviceId来确定当期服务该调用何种类型的caller；
		// 为了简化起见，内部系统尽量采用统一方式的RPC调用方式
		return caller;
	}

	/**
	 * 获取完整调用结果
	 * 
	 * @param serviceId
	 * @param param数组
	 *            ，注意参数顺序
	 * @return
	 */
	public static String callFullResult(String serviceId, Object... param) {
		String result = null;
		try {
			long start = System.currentTimeMillis();
			result = getCaller(serviceId).call(serviceId, param);
			System.out.println("耗时：" + (System.currentTimeMillis() - start));
		} catch (Exception e) {
			logger.error(e);
			// 调用异常处理..todo
			throw new RuntimeException(e);
		}
		return result;
	}

	/**
	 * 调用的远程服务如果没有返回结果，则用此方法
	 * 
	 * 如果调用过程正常，则返回true，如果有错误，则抛出运行时异常
	 * 
	 * @param serviceId
	 * @param param数组
	 *            ，注意参数顺序
	 * @return
	 */
	public static boolean callForVoid(String serviceId, Object... param) {
		String fullResult = callFullResult(serviceId, param);
		JSONObject json = JSONObject.parseObject(fullResult);
		if (json.getBoolean("success") == false) {
			throw new RuntimeException("获取结果出错：" + json.getString("desc"));
		}
		return true;
	}

	/**
	 * 由于目标类型为list和page等类型是，处理不一样，该方法暂时不对外公开 获取字符串结果
	 * 
	 * @param serviceId
	 * @param param
	 * @return String
	 * @throws
	 */
	private static <T> T callForTarget(String serviceId, Class<T> targetClass, Object... param) {
		String fullResult = callFullResult(serviceId, param);
		JSONObject json = JSONObject.parseObject(fullResult);
		if (json.getBoolean("success")) {
			return JSONUtil.toTarget(json.getString("result"), targetClass);
		} else {
			throw new RuntimeException("获取结果出错：" + json.getString("desc"));
		}
	}

	public static JSONArray callForJSONArray(String serviceId, Object... param) {
		JSONArray jsonArray = callForTarget(serviceId, JSONArray.class, param);
		return jsonArray;
	}

	public static BigDecimal callForBigDecimal(String serviceId, Object... param) {
		return callForTarget(serviceId, BigDecimal.class, param);
	}

	public static BigInteger callForBigInteger(String serviceId, Object... param) {
		return callForTarget(serviceId, BigInteger.class, param);
	}

	public static Date callForDate(String serviceId, Object... param) {
		return callForTarget(serviceId, Date.class, param);
	}

	public static Boolean callForBoolean(String serviceId, Object... param) {
		return callForTarget(serviceId, Boolean.class, param);
	}

	public static Character callForCharacter(String serviceId, Object... param) {
		return callForTarget(serviceId, Character.class, param);
	}

	public static Byte callForByte(String serviceId, Object... param) {
		return callForTarget(serviceId, Byte.class, param);
	}

	public static Short callForShort(String serviceId, Object... param) {
		return callForTarget(serviceId, Short.class, param);
	}

	public static String callForString(String serviceId, Object... param) {
		return callForTarget(serviceId, String.class, param);
	}

	public static Integer callForInteger(String serviceId, Object... param) {
		return callForTarget(serviceId, Integer.class, param);
	}

	public static Long callForLong(String serviceId, Object... param) {
		return callForTarget(serviceId, Long.class, param);
	}

	public static Float callForFloat(String serviceId, Object... param) {
		return callForTarget(serviceId, Float.class, param);
	}

	/**
	 * 获取JSONObject结果
	 * 
	 * @param serviceId
	 * @param param
	 * @return JSONObject
	 * @throws
	 */

	public static JSONObject callForJSON(String serviceId, Object... param) {
		String jsonStr = callForString(serviceId, param);
		System.out.println(jsonStr);
		return JSONUtil.toEntity(jsonStr, JSONObject.class);
	}

	/**
	 *获取指定类型的结果
	 * 
	 * @param <T>
	 * @param serviceId
	 * @param param
	 * @param targetClass
	 * @return T
	 * @throws
	 */
	public static <T> T callForEntity(String serviceId, Class<T> targetClass, Object... param) {
		return callForTarget(serviceId, targetClass, param);
	}

	/**
	 * 获取指定类型的List
	 * 
	 * @param <T>
	 * @param serviceId
	 * @param param
	 * @param targetClass
	 * @return T
	 * @throws
	 */
	public static <T> List<T> callForList(String serviceId, Class<T> targetClass, Object... param) {
		String str = callForString(serviceId, param);
		List result = JSONUtil.toList(str, targetClass);
		return result;
	}

	/**
	 * 获取分页结果
	 * 
	 * @param <T>
	 * @param serviceId
	 * @param param
	 * @param targetClass
	 * @return T
	 * @throws
	 */
	public static <T> Page<T> callForPage(String serviceId, Class<T> targetClass, Object... param) {
		JSONObject json = callForJSON(serviceId, param);
		Page<T> result = new Page<T>();
		result.setPageNo(json.getIntValue("pageNo"));
		result.setPageSize(json.getIntValue("pageSize"));
		result.setTotalCount(json.getLong("totalCount"));
		result.setTotalPages(json.getIntValue("totalPages"));
		List<T> list = JSONObject.parseArray(json.getString("result"), targetClass);
		result.setResult(list);
		return result;
	}
}
